package com.ml.netty.simple;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * Netty快速入门实例-TCP服务
 * 实例要求：
 * 1) Netty 服务器在 6668 端口监听，客户端能发送消息给服务器 "hello, 服务器~"
 * 2) 服务器可以回复消息给客户端 "hello, 客户端~"
 * 3) 目的：对Netty 线程模型 有一个初步认识, 便于理解Netty 模型理论
 *
 * @author xr-8
 * @date 2022-01-23
 * @time 16:09
 */
public class NettyServer {

    public static void main(String[] args) {

        //创建bossGroup和workerGroup
        //说明:
        //1.创建两个线程组bossGroup和workerGroup
        //2.bossGroup只是处理连接请求，真正的客户端业务处理会交给workerGroup完成
        //3.两个都是无限循环
        //4.bossGroup和workerGroup含有子线程(NioEventLoop)的个数默认=实际cpu核数*2
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            //创建服务器端的启动对象，配置参数
            ServerBootstrap bootstrap = new ServerBootstrap();

            bootstrap.group(bossGroup, workerGroup)//设置两个线程组
                    .channel(NioServerSocketChannel.class)//使用NioServerSocketChannel作为服务器的通道实现
                    .option(ChannelOption.SO_BACKLOG, 128)//设置线程队列得到连接个数
                    .childOption(ChannelOption.SO_KEEPALIVE, true)//设置保持活动连接状态
//                    .handler(null) //该handler对应bossGroup, childHandler对应workerGroup
                    .childHandler(new ChannelInitializer<SocketChannel>() {//创建一个通道初始化对象
                        //给pipeline设置处理器
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {//创建一个通道初始化对象（匿名对象）

                            ch.pipeline().addLast(new NettyServerHandler());

                            //下面的文字说明是针对 task三种模型中的第三种。
                            //可以使用一个集合管理SocketChannel，再推送消息时，可以将业务加入到各个channel对应的NIOEventLoop的taskQueue或者scheduleTaskQueue中。
                            System.out.println("客户端socketChannel hashcode=" + ch.hashCode());
                        }
                    });

            System.out.println("服务器 is ready...");
            //绑定一个端口并且同步，生成了一个ChannelFuture对象
            //启动服务器并且绑定端口
            ChannelFuture channelFuture = bootstrap.bind(6668).sync();
            //给cf注册监听器，监听我们感兴趣的事件
            channelFuture.addListener((ChannelFutureListener) future -> {
                if (channelFuture.isSuccess()) {
                    System.out.println("监听端口 6668 成功");
                } else {
                    System.out.println("监听端口 6668 失败");
                }
            });
            //对关闭通道进行监听
            channelFuture.channel().closeFuture().sync();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
